package com.zf.bm.common.core.sys.service;

import com.baomidou.dynamic.datasource.annotation.DS;
import com.google.common.collect.Maps;
import com.zf.bm.common.core.base.service.BaseService;
import com.zf.bm.common.core.log.annotation.SysLog;
import com.zf.bm.common.core.log.annotation.SysLogTitle;
import com.zf.bm.common.core.sys.dao.LogDao;
import com.zf.bm.common.core.sys.entity.Log;
import com.zf.bm.common.core.sys.entity.User;
import com.zf.bm.common.utils.HttpUtils;
import com.zf.bm.common.utils.Result;
import com.zf.bm.common.utils.UserUtils;
import com.zf.bm.common.utils.mapper.JsonMapper;
import io.netty.util.internal.ThrowableUtil;
import org.apache.shiro.web.servlet.ShiroHttpServletRequest;
import org.apache.shiro.web.servlet.ShiroHttpServletResponse;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.support.StandardMultipartHttpServletRequest;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.Date;
import java.util.Map;
import java.util.Objects;

/**
 * 日志Service
 */
@Service
@DS("master")
@Transactional
public class LogService extends BaseService<LogDao, Log> {

    public void saveLog(String type, Long runTime, ProceedingJoinPoint joinPoint,
                        HttpServletRequest request, Object result) {
        saveLog(type, runTime, null, joinPoint, request, result);
    }

    @Async
    public void saveLog(String type, Long runTime, Throwable throwable,
                        ProceedingJoinPoint joinPoint, HttpServletRequest request, Object result) {
        User user = UserUtils.getUser();
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        Log log = new Log();
        SysLog aopLog = method.getAnnotation(SysLog.class);
        Map<String, Object> paramMap = Maps.newConcurrentMap();
        addParam(joinPoint, log, aopLog, paramMap);
        // 方法路径
        String methodName = joinPoint.getTarget().getClass().getName() + "." + signature.getName() + "()";
        if (null != throwable) {
            log.setException(ThrowableUtil.stackTraceToString(throwable));
        }

        log.setType(type);
        log.setRunTime(runTime);
        log.setRemoteAddr(HttpUtils.getRemoteAddr() == null ? "" : HttpUtils.getRemoteAddr());
        log.setUserAgent(request.getHeader("user-agent"));
        log.setRequestUri(request.getMethod() + " " + request.getRequestURI());

        setParam(log, paramMap);
        log.setMethod(methodName);
        String msg = "成功";
        if (result instanceof Result) {
            Result re = (Result) result;
            if (Objects.isNull(re.getData())) {
                msg = re.getMessage();
            }

        }
        if (type.equals(Log.TYPE_EXCEPTION)) {
            msg = "失败";
        }
        log.setRemarks(msg);
        if (Objects.nonNull(user)) {
            log.setCreateBy(user);
            log.setUpdateBy(user);
        }
        log.setUpdateDate(new Date());
        log.setCreateDate(new Date());
        save(log);
    }

    private static void setParam(Log log, Map<String, Object> paramMap) {
        if (paramMap.isEmpty()) {
            return;
        }
        boolean isMultiPartFile = false;
        for (Object o : paramMap.values()) {
            if (o instanceof StandardMultipartHttpServletRequest || o instanceof MultipartFile) {
                isMultiPartFile = true;
                break;

            }
        }
        if (!isMultiPartFile) {
            log.setParams(JsonMapper.toJsonString(paramMap));
        }
    }

    private void addParam(ProceedingJoinPoint joinPoint, Log log, SysLog aopLog, Map<String, Object> paramMap) {
        if (aopLog == null) {
            return;
        }
        log.setTitle(getTitle(joinPoint, aopLog));
        //参数值
        Object[] argValues = joinPoint.getArgs();
        //参数名称
        String[] argNames = ((MethodSignature) joinPoint.getSignature()).getParameterNames();
        if (argValues == null) {
            return;
        }
        for (int i = 0; i < argValues.length; i++) {
            if (argValues[i] != null && !(argValues[i] instanceof ShiroHttpServletRequest ||
                    argValues[i] instanceof ShiroHttpServletResponse)) {
                paramMap.put(argNames[i], argValues[i]);
            }
        }
    }


    @Override
    public boolean save(Log entity) {
        User user = entity.getCreateBy();
        if (null != user && !user.isAdmin()) {
            return super.save(entity);
        }
        return true;
    }

    private String getTitle(ProceedingJoinPoint joinPoint, SysLog aopLog) {
        SysLogTitle logTitle = joinPoint.getTarget().getClass().getAnnotation(SysLogTitle.class);
        String title = logTitle==null?"":logTitle.value();
        return title+aopLog.value();
    }
}
